/**
 * Classe Noeud 
 * @author S Jousselin / M Quin / V Delcroix
 */

package fonctionnel;

import technique.NoeudIntf;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.UnknownHostException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.io.*;

public class Noeud implements Serializable{
    private String repertoire;
    private String adresseIP;
    private ArrayList<Fichier> listeFichier;
    private int nivConfiance;
    private ArrayList<NoeudConfiance> listeNoeudConf;

    /**
     * Constructeur vide de la classe Noeud
     */
    public Noeud()
    {
        this.repertoire=null;
        this.adresseIP=null;
        this.listeFichier=new ArrayList<Fichier>();
        this.nivConfiance=0;
        this.listeNoeudConf = new ArrayList<NoeudConfiance>();
    }
    /**
     * Constructeur de la classe noeud
     * @param adresseIP  String : adresse IP du noeud
     * @param repertoire String : répertoire du noeud
     */
    public Noeud(String adresseIP, String repertoire)
    {
        this.repertoire = repertoire;
        this.adresseIP = adresseIP;
        this.listeFichier = new ArrayList<Fichier>();
        this.nivConfiance = 0;
        this.listeNoeudConf = new ArrayList<NoeudConfiance>();
    }

    public ArrayList<Fichier> getListeFichier(){
        return this.listeFichier;
    }

    public ArrayList<NoeudConfiance> getListeNC(){
        return this.listeNoeudConf;
    }

    public int getDegreConf(){
        return this.nivConfiance;
    }



    /**
     * Liste l'ensemble des fichiers présents dans le répertoire
     */
    public void listerFichier()
    {
        File path = new File(this.repertoire);

        if (path.exists())
        {
            // Récupère tous les fichiers dans un tableau
            File[] files = path.listFiles();
            for(int i=0; i < files.length; i++)
            {

                // Si c'est bien un fichier !!
                if(files != null && files[i].isFile() == true)
                {
                    // On va chercher dans la liste des fichiers son degré de confidentialité
                    if(!this.listeFichier.isEmpty())
                    {
                        for(Fichier fic : listeFichier)
                        {
                            if(fic.getNom().equals(files[i].getName()))
                            {
                                System.out.println("Nom du fichier : " + files[i].getName() + " Degré de confidentialité : " + fic.getDegreConf());
                            }
                            else System.out.println("Nom du fichier : " + files[i].getName() + " Pas de confidentialité.");
                        }
                    }
                    else System.out.println("Nom du fichier : " + files[i].getName() + " Pas de confidentialité.");
                }
            }
        }
        else
        {
            System.out.println("! Répertoire non trouvé !");
        }
    }

    /**
     * Retourne l'index du fichier, dont le nom est passé en paramètre, de la listeFichier
     * @param nomFic String : nom du fichier
     * @return index int : index du fichier dans la liste des fichiers
     */
    private int rechercheIndexFichier(String nomFic)
    {
        for(Fichier fic : listeFichier)
        {
            if(fic.getNom().equals(nomFic))
            {
                return this.listeFichier.indexOf(fic);
            }
        }
        // Si le noeud n'existe pas
        return -1;
    }

    /**
     * Retourne l'index du noeud dont l'adresse IP est passée en paramètre, de la liste des noeuds de confiance
     * @param adrIP int : adresse IP du noeud de confiance
     * @return index int : index du noeud dans la liste des noeuds de confiance
     */
    private int rechercheIndexNoeudConf(String adrIP)
    {
        for(NoeudConfiance noeudC : this.listeNoeudConf)
        {
            if(noeudC.getAdrIP().equals(adrIP))
            {
                   return this.listeNoeudConf.indexOf(noeudC);
            }
        }
        // Si le noeud n'existe pas dans la liste
        return -1;
    }

    /**
     * Cette fonction crée un objet Noeud de confiance, vérifie son existence dans l'annuaire, appel la fct AjoutNoeudListeConfiance
     * afin d'ajouter le noeud créé à la liste des noeuds de confiance, puis entraîne la copie de tous les fichiers du répertoire
     * à sauvegarder sur le nouveau noeud de confiance si les degrés sont compatibles.
     */
    public void ajoutNoeudConfiance()
    {
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String adrIP="";
        System.out.println("Veuillez saisir l'adresse IP du noeud de confiance");
        try
        {
            adrIP= clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }

        if(verifExistenceNoeud(adrIP))
        {
            if(!this.verifNoeudListeConfiance(adrIP))
            {
                int nivConf=0;
                boolean ok=false;
                while(!ok)
                {
                    System.out.println("Veuillez saisir le degré de confiance du noeud de confiance (0 ou 1 ou 2 ou 3)");
                    try
                    {
                        nivConf= Integer.parseInt(clavier.readLine());
                    }
                    catch(Exception e)
                    {
                        System.out.println(e.getMessage());
                    }

                    if(nivConf ==0 || nivConf ==1 ||nivConf ==2 ||nivConf ==3) ok=true;

                }
                NoeudConfiance n = new NoeudConfiance(adrIP,nivConf);
                ajoutNoeudListeConfiance(n);
                System.out.println("Noeud ajouté avec succès");
                this.dupliquer(n);
            }
            else
            {
                this.dupliquer(this.listeNoeudConf.get(this.rechercheIndexNoeudConf(adrIP)));
            }
        }
        else
        {
            System.out.println("L'adresse IP n'est pas connue");
        }
    }

    /**
     * Cette fonction permet d'ajouter un Noeud de Confiance dans la liste des Noeuds de confiance
     * @param n : objet noeudConfiance
     */
    public void ajoutNoeudListeConfiance(NoeudConfiance n)
    {
        this.listeNoeudConf.add(n);
    }

    /**
     * Cette fonction est déclenchée lors de l'ajout d'un noeud à la liste des noeuds de confiance
     * elle parcourt le tableau des fichiers à sauvegarder et déclenche la fonction dupliquer du côté du noeud de confiance si les degrés sont cohérents
     * afin de copier le fichier sur le noeud de confiance
     * @param NC Noeud de confiance : noeud de confiance à ajouter à la liste des noeud de confiance
     */
    private void dupliquer(NoeudConfiance NC)
    {
       if(!this.listeFichier.isEmpty())
       {
            try
            {
                Registry registry = LocateRegistry.getRegistry(NC.getAdrIP(), 1099);
                NoeudIntf refObjDistant = (NoeudIntf)registry.lookup(NC.getAdrIP());

                for(Fichier fic : this.listeFichier)
                {
                    // On verifie si le fichier à une liste de noeuds de confiance
                    // Si la liste des noeuds de confiance du fichier est vide
                    if(fic.getListeNoeudConf().isEmpty())
                    {
                        // On vérifie si le degré de confiance du fichier est inferieur à celui du noeud de confiance
                        if(fic.getDegreConf()<=NC.getNivConfiance())
                        {
                            String Retour="";
                            try
                            {
                                    // Lecture du fichier
                                    FileReader ficSource = new FileReader(this.repertoire + "\\"+fic.getNom());

                                    BufferedReader ligneLu = new BufferedReader(ficSource);
                                    String ligne;
                                    do
                                    {
                                            ligne = ligneLu.readLine();
                                            if (ligne != null){
                                                    Retour+=(ligne+"\n");
                                            }
                                    } while (ligne != null);
                                    ligneLu.close();
                                    // Envoi le contenu du fichier au noeud de confiance
                                    refObjDistant.dupliquer(this.adresseIP,fic.getNom(), Retour);
                            }
                            catch(FileNotFoundException e)
                            {
                                    System.out.println(e);
                            }
                            catch (IOException e)
                            {
                                    System.out.println(e);
                            }
                        }
                    }
                    // Si la liste des noeuds de confiance du fichier n'est pas vide
                    else
                    {
                        // Si le noeud appartient à la liste des noeuds
                        if(fic.getListeNoeudConf().contains(NC))
                        {
                            String Retour="";
                            try
                            {
                                    // Lecture du fichier
                                    FileReader ficSource = new FileReader(this.repertoire + "\\"+fic.getNom());

                                    BufferedReader ligneLu = new BufferedReader(ficSource);
                                    String ligne;
                                    do
                                    {
                                            ligne = ligneLu.readLine();
                                            if (ligne != null){
                                                    Retour+=(ligne+"\n");
                                            }
                                    } while (ligne != null);
                                    ligneLu.close();
                                    // Envoie du contenu du fichier au noeud de confiance
                                    refObjDistant.dupliquer(this.adresseIP,fic.getNom(), Retour);
                            }
                            catch(FileNotFoundException e)
                            {
                                    System.out.println(e);
                            }
                            catch (IOException e)
                            {
                                    System.out.println(e);
                            }
                        }
                    }
                }
            }
            catch(Exception e)
            {
                 System.out.println(e);
            }
        }
    }

    /**
     * Supprime un noeud à la liste des noeuds de confiance
     * Parcours de la liste des noeuds de confiance, une fois le noeud trouvé on le supprime !
     *
     * @param AdrIP String : adresse IP du noeud à supprimer
     */
    private void supprimerNoeud(String adrIP)
    {
        if(this.verifExistenceNoeud(adrIP))
        {
            if(this.verifNoeudListeConfiance(adrIP))
            {
                this.listeNoeudConf.remove(this.rechercheIndexNoeudConf(adrIP));
                System.out.println("Noeud de confiance supprimé");
            }
            else
            {
                System.out.println("Noeud inexistant dans la liste");
            }
        }
        else
        {
            System.out.println("Noeud inexistant");
        }
    }

    /**
     * Fonction qui demande l'adresse ip du noeud à supprimer et supprime le noeud si le noeud existe dans la liste
     */
    public void suppressionNoeudConfiance()
    {
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String adrIp="";
        System.out.println("Adresse IP du noeud à supprimer : ");
        try
        {
            adrIp= clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        this.supprimerNoeud(adrIp);
    }

    /**
     * Vérifie l'existence du fichier dont le nom est passé en paramère dans la liste des fichiers
     *
     * @param nomFichier String : nom du fichier à vérifier
     * @return boolean : true si le fichier existe dans la liste des fichiers, false sinon
     */
    private boolean verifExistenceListeFichier(String nomFichier)
    {
        for(Fichier fic : listeFichier)
        {
            if(fic.getNom().equals(nomFichier))
            {
                    return true;
            }
        }
        return false;
    }

    /**
     * Fonction qui vérifie l'existence d'un fichier dans le répertoire, dont le nom est passé en paramètre
     * @param nomFichier String : nom du fichier
     * @return boolean : true si le fichier existe dans le répertoire, false sinon
     */
    private boolean verifExistenceFichier(String nomFichier)
    {
       File rep = new File(this.repertoire);

       if (rep.isDirectory())
       {
           File[] list = rep.listFiles();
           if (list != null)
           {
               for ( int i = 0; i < list.length; i++)
               {
                   if (!list[i].isDirectory())
                   {
                       if(list[i].getName().equals(nomFichier))
                           return true;
                   }
               }
           }
           else
           {
               System.err.println(repertoire + " : Erreur de lecture.");
           }
        }
        return false;
    }


    /**
     * Enchaînement pour assigner un degré à une fonction
     */
    public void assignerDegre()
    {
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String nomFic="";
        System.out.println("Nom du fichier (avec l'extension) : ");
        try
        {
            nomFic= clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        if(verifExistenceFichier(nomFic))
        {
            boolean ok=false;
            int degre=20;
            while(!ok)
            {
                System.out.println("Degré de confidentialité du fichier : (0 ou 1 ou 2 ou 3)");
                try
                {
                    degre = Integer.parseInt(clavier.readLine());
                }
                catch(Exception e)
                {
                    System.out.println(e.getMessage());
                }
                // Vérifie si le degré rentré par l'utilisateur fait bien partie de la liste des degrés
                if(degre==0||degre==1||degre==2||degre==3)
                    ok=true;

            }
            this.assignDegre(nomFic, degre);
        }
        else
            System.out.println("fichier inexistant!");

    }

    /**
     * Assigne le degré à un fichier passé en parametre
     * @param nomFichier String : nom du fichier
     * @param degre int : degré de confidentialié du fichier
     */
    public void assignDegre(String nomFichier, int degre)
    {
        if(this.verifExistenceListeFichier(nomFichier))
        {
            this.modifDegre(nomFichier, degre);
        }
        else
        {
            this.ajoutFichierListe(nomFichier, degre);
        }
    }

    /**
     * Modifie le degré d'un fichier
     * @param nomFichier String : nom du fichier
     * @param degre int : degré de confidentialité du fichier
     */
    private void modifDegre(String nomFichier, int degre)
    {
        int i=0;
        for(Fichier fic : listeFichier)
        {
            if(fic.getNom().equals(nomFichier))
            {
                i=this.listeFichier.indexOf(fic);
            }
        }
        this.listeFichier.get(i).setDegreConf(degre);
    }

    /**
     * Ajout un fichier à la liste des fichiers
     * @param nomFichier String : nom du fichier
     * @param degre int : degré de confidentialité du fichier
     */
    private void ajoutFichierListe(String nomFichier, int degre)
    {
        Fichier fic=new Fichier(nomFichier, degre);
        this.listeFichier.add(fic);
    }

    /**
     * On vérifie dans le registry si le noeud est connecté
     * @param adrIP String : adresse IP du noeud
     * @return boolean : true si le noeud existe, false sinon
     */
    private boolean verifExistenceNoeud(String adrIP)
    {
        try
        {
            Registry registry = LocateRegistry.getRegistry(adrIP, 1099);
            NoeudIntf refObjDistant = (NoeudIntf)registry.lookup(adrIP);
        }
        catch (NotBoundException e) 
        {
            return false;
        }
        catch (AccessException e)
        {
            return false;
        }
        catch(UnknownHostException e)
        {
            return false;
        }
        catch (RemoteException e)
        {
            return false;
        }
        return true;
    }

    public void recuperationVersionFichier()
    {
        //saisie nom fichier
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String nomFic="";
        int degre=0;
        boolean trouve =false;
        System.out.println("Nom du fichier à récupérer (avec l'extension) : ");
        try
        {
            nomFic= clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        // Recherche degré de confidentialité
        for(Fichier fic : listeFichier)
        {
            if(fic.getNom().equals(nomFic))
            {
                    degre = fic.getDegreConf();
                    trouve= true;
            }
        }
        // Recherche un noeud de confiance dont le degré de confiance est supérieur pour récupérer le fichier
        if(trouve)
        {
            String AdrIP="";
            trouve = false;
            for(NoeudConfiance NC : this.listeNoeudConf)
            {
                if(NC.getNivConfiance()>= degre)
                {
                    if(!trouve)
                    {
                        AdrIP = NC.getAdrIP();
                        if(this.verifExistenceNoeud(AdrIP))
                        {
                            trouve = true;
                        }
                    }
                }
            }
            if(!trouve)System.out.println("Pas de noeud de confiance connecté ou ayant le bon degré de confiance");
            else recupererVersion(AdrIP, nomFic);
        }
        else
        {
            System.out.println("Le fichier n'est pas connu du système");
        }
    }

    /**
     * Fonction qui récupère la version du fichier passé en paramètre sur le noeud de confiance
     * @param adrIp String : adresse ip du noeud sur lequel on veut récupèrer la version
     * @param nomFic String : nom du fichier voulu
     */
    public void recupererVersion(String adrIp, String nomFic)
    {
        try
        {
            Registry registry = LocateRegistry.getRegistry(adrIp, 1099);
            NoeudIntf refObjDistant = (NoeudIntf)registry.lookup(adrIp);
            System.out.println("Veuillez choisir une version de fichier (nom complet du fichier avec l'extension) :");
            System.out.println(refObjDistant.listeVersion(this.adresseIP, nomFic));
            BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
            String nomFicVersion="";
            try
            {
                nomFicVersion= clavier.readLine();
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
            }

            String contenu=refObjDistant.recupVersion(this.adresseIP, nomFicVersion);

            FileWriter f = new FileWriter(this.repertoire + "\\" + nomFic, false);
            BufferedWriter sortie = new BufferedWriter(f);
            String lecture=contenu.replaceAll("\n", "\r\n");
            sortie.write(lecture);
            sortie.flush();
            sortie.close();
            f.close();
        }
        catch (Exception e)
        {
            System.out.println(e);

        }
    }

    /**
     * Fonction pour récupèrer un fichier perdu. Récupère la dernière version du
     * fichier sur le premier noeud de la liste des noeuds de confiance connectés
     */
    public void recupFichier()
    {
        // Saisie nom du fichier
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String nomFic="";
        int degre=0;
        boolean trouve =false;
        System.out.println("Nom du fichier a recuperer (avec l'extension) : ");
        try
        {
            nomFic= clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }

        // Recherche degré de confidentialité
        for(Fichier fic : listeFichier)
        {
            if(fic.getNom().equals(nomFic))
            {
                    degre = fic.getDegreConf();
                    trouve= true;
            }
        }
        // Recherche un noeud de confiance dont le degré de confiance est supérieur pour récupérer le fichier
        if(trouve)
        {
            String AdrIP="";
            trouve = false;
            for(NoeudConfiance NC : this.listeNoeudConf)
            {
                if(NC.getNivConfiance()>= degre)
                {
                    if(!trouve)
                    {
                        AdrIP = NC.getAdrIP();
                        if(this.verifExistenceNoeud(AdrIP))
                        {
                            trouve = true;
                        }
                    }
                }
            }
            if(!trouve)System.out.println("Le fichier n'est pas connu du système");
            else recuperer(AdrIP, nomFic);
        }
        else
        {
            System.out.println("Le fichier n'est pas connu du système");
        }
    }

    /**
     * Fonction qui récupère le fichier sur le noeud dont l'adresse IP est passé en paramètre
     * @param AdrIP String : adresse IP du noeud
     * @param nomFic String : nom du fichier à récupérer
     */
    private void recuperer(String AdrIP, String nomFic)
    {
        try
        {
            Registry registry = LocateRegistry.getRegistry(AdrIP, 1099);
            NoeudIntf refObjDistant = (NoeudIntf)registry.lookup(AdrIP);
            String contenu=refObjDistant.recup(this.adresseIP, nomFic);
            if(!contenu.equals(""))
            {
                String lecture="";
                FileWriter f = new FileWriter(this.repertoire + "\\" + nomFic, false);
                BufferedWriter sortie = new BufferedWriter(f);
                lecture=contenu.replaceAll("\n", "\r\n");
                sortie.write(lecture);
                sortie.flush();
                sortie.close();
                f.close();
            }
            else
            {
                System.out.println("Pas de sauvegarde du fichier");
            }

        }
        catch (Exception e)
        {
            System.out.println(e);
        }        
    }

    /**
     * Vérifie qu'un noeud (par son @IP) existe ou non dans la liste de confiance
     * 
     * @param adrIP String : adresse IP du noeud
     * @return boolean : true si le noeud existe dans la liste de confiance, false sinon.
     *
     */
    private boolean verifNoeudListeConfiance(String adrIP)
    {
        boolean trouve = false;
        for(NoeudConfiance noeudC : this.listeNoeudConf)
        {
            try
            {
                if(noeudC.getAdrIP().equals(adrIP))
                {
                   trouve = true;
                }
            }
            catch(Exception e)
            {

                System.out.println(e.getMessage());
            }
        }
        return trouve;
    }

    /**
     * Enchainement pour gérer les autorisations par fichiers
     */
     public void gestionAutorisationsParFichier()
     {
        BufferedReader clavier = new BufferedReader(new InputStreamReader(System.in));
        String nomFic = "";
        String adrIP = "";
        System.out.println("Nom du fichier (avec l'extension) : ");
        try
        {
            nomFic = clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        if(verifExistenceListeFichier(nomFic))
        {
            clavier = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Saisir l'adresse IP :");
            try
            {
                adrIP = clavier.readLine();

                if(verifNoeudListeConfiance(adrIP))
                {
                    this.listeFichier.get(this.rechercheIndexFichier(nomFic)).ajouterNoeudListeConf(this.listeNoeudConf.get(this.rechercheIndexNoeudConf(adrIP)));
                    System.out.println("Noeud ajouté");
                }
                else System.out.println("Noeud inexistant");
            }
            catch(Exception e)
            {
                System.out.println(e.getMessage());
            }
        }
        else System.out.println("Fichier inexistant!");

    }

     /**
     * Nettoie les fichiers n'étant plus dans le répertoire
     */
     public void nettoyerFicRep()
     {
        String nomFic = "";
        BufferedReader clavier = new BufferedReader (new InputStreamReader(System.in));
        System.out.println("Nom du fichier (avec l'extension) : ");
        try
        {
            nomFic = clavier.readLine();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
        for(NoeudConfiance noeudC : this.listeNoeudConf)
        {
            if(!verifExistenceNoeud(noeudC.getAdrIP()))
            {
                // Supprimer ce noeud à la liste des noeuds de confiance, car il n'est plus connecté
                supprimerNoeud(noeudC.getAdrIP());
            }
            else
            {
                try
                {
                    Registry registry = LocateRegistry.getRegistry(noeudC.getAdrIP(), 1099);
                    NoeudIntf refObjDistant = (NoeudIntf)registry.lookup(noeudC.getAdrIP());
                    refObjDistant.supprimerFichier(this.adresseIP, nomFic);
                }
                catch(Exception e)
                {
                    System.out.println(e);
                }
            }
        }
    }

    @Override
    public String toString()
    {
        StringBuilder sb= new StringBuilder();
        sb.append("adresse ip : ");
        sb.append(this.adresseIP);
        sb.append("\nrepertoire : ");
        sb.append(this.repertoire);
        sb.append("\nniveau de confiance : ");
        sb.append(this.nivConfiance);
        return sb.toString();
    }


}
